package MiniC.Parser;

import MiniC.Scanner.Scanner;
import MiniC.Scanner.Token;
import MiniC.Scanner.SourcePos;
import MiniC.Parser.SyntaxError;
import MiniC.ErrorReporter;
import MiniC.AstGen.*;


public class jun_Parser {

    private Scanner scanner;
    private ErrorReporter errorReporter;
    private Token currentToken;
    private SourcePos previousTokenPosition;

    public jun_Parser(Scanner lexer, ErrorReporter reporter) {
		scanner = lexer;
        errorReporter = reporter;
    }

    // accept() checks whether the current token matches tokenExpected.
    // If so, it fetches the next token.
    // If not, it reports a syntax error.
    void accept (int tokenExpected) throws SyntaxError {
	if (currentToken.kind == tokenExpected) {
        previousTokenPosition = currentToken.GetSourcePos();
	    currentToken = scanner.scan();
	} else {
	    syntaxError("\"%\" expected here", Token.spell(tokenExpected));
	}
    }

    // acceptIt() unconditionally accepts the current token
    // and fetches the next token from the scanner.
    void acceptIt() {
        previousTokenPosition = currentToken.GetSourcePos();
		currentToken = scanner.scan();
    }

    // start records the position of the start of a phrase.
    // This is defined to be the position of the first
    // character of the first token of the phrase.
    void start( SourcePos position ) {
        position.StartCol = currentToken.GetSourcePos().StartCol;
        position.StartLine = currentToken.GetSourcePos().StartLine;
    }

    // finish records the position of the end of a phrase.
    // This is defined to be the position of the last
    // character of the last token of the phrase.
    void finish( SourcePos position ) {
        position.EndCol = previousTokenPosition.EndCol;
        position.EndLine = previousTokenPosition.EndLine;
    }

    void syntaxError(String messageTemplate, String tokenQuoted) throws SyntaxError {
		SourcePos pos = currentToken.GetSourcePos();
		errorReporter.reportError(messageTemplate, tokenQuoted, pos);
		throw( new SyntaxError( ) );
    }

    boolean isTypeSpecifier(int token) {
		if(token == Token.VOID ||
           token == Token.INT  ||
           token == Token.BOOL ||
           token == Token.FLOAT) {
	    	return true;
		} else {
	    	return false;
		}
    }

	/*
		Me
	*/
	boolean isLiteral( int token ) {

		if( token == Token.INTLITERAL	|| 
			token == Token.FLOATLITERAL	||
			token == Token.BOOLLITERAL	||
			token == Token.STRINGLITERAL ) {

			return true;
		} else {
			return false;
		}
	}

	boolean isComparisonOP( int token ) {

		if( token == Token.EQ		||
			token == Token.NOTEQ	||
			token == Token.LESSEQ	||
			token == Token.LESS		||
			token == Token.GREATER	||
			token == Token.GREATEREQ ) {

			return true;
		} else {
			return false;
		}

	}

    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseArrayIndexDecl (Type T):
    //
    // Take [INTLITERAL] and generate an ArrayType
    //
    ///////////////////////////////////////////////////////////////////////////////

    public ArrayType parseArrayIndexDecl(Type T) throws SyntaxError {
		
		IntLiteral L;
		IntExpr IE;
		
		accept(Token.LEFTBRACKET);
        SourcePos pos = currentToken.GetSourcePos();
		L = new IntLiteral(currentToken.GetLexeme(), pos);
		
		accept(Token.INTLITERAL);
		accept(Token.RIGHTBRACKET);
		
		IE = new IntExpr (L, pos);
		
		return new ArrayType (T, IE, previousTokenPosition);
    }


    ///////////////////////////////////////////////////////////////////////////////
    //
    // toplevel parse() routine:
    //
    ///////////////////////////////////////////////////////////////////////////////

    public Program parse() {

        Program ProgramAST = null;

        previousTokenPosition = new SourcePos();
        previousTokenPosition.StartLine = 0;
        previousTokenPosition.StartCol = 0;
        previousTokenPosition.EndLine = 0;
        previousTokenPosition.EndCol = 0;

		currentToken = scanner.scan(); // get first token from scanner...

		try {
            ProgramAST = parseProgram();
	    	if (currentToken.kind != Token.EOF) {
				syntaxError("\"%\" not expected after end of program",
			    currentToken.GetLexeme());
	    	}
		}
		catch (SyntaxError s) { 
			return null;
		}
		
		return ProgramAST;
    }

    
    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseProgram():
    //
    // program ::= ( (VOID|INT|BOOL|FLOAT) ID ( FunPart | VarPart ) )* ";"
    //
    ///////////////////////////////////////////////////////////////////////////////

    // parseProgDecls: recursive helper function to facilitate AST construction.
    public Decl parseProgDecls () throws SyntaxError {
        
		if ( !isTypeSpecifier( currentToken.kind ) ) {
           return new EmptyDecl ( previousTokenPosition );
        }
        
		SourcePos pos = new SourcePos();
        start(pos);
        
		Type T = parseTypeSpec();
		ID Ident = parseID();
	
		if( currentToken.kind == Token.LEFTPAREN ) {
	   		Decl newD = parseFunPart(T, Ident, pos);
			return new DeclSequence ( newD, parseProgDecls(), previousTokenPosition );
		} else {
			DeclSequence Vars = parseVarPart(T, Ident);
           	DeclSequence VarsTail = Vars.GetRightmostDeclSequenceNode();
           	Decl RemainderDecls = parseProgDecls();
           	VarsTail.SetRightSubtree (RemainderDecls);
           	return Vars;
		}
    }

    public Program parseProgram() throws SyntaxError {
        SourcePos pos = new SourcePos();
        start( pos );
        Decl D = parseProgDecls();
        finish( pos );
        Program P = new Program ( D, pos );
        return P;
    }


    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseTypeSpec():
    //
    // TypeSpec ::= VOID|INT|BOOL|FLOAT
    //
    ///////////////////////////////////////////////////////////////////////////////

    public Type parseTypeSpec() throws SyntaxError {
        Type T = null;
		
		switch( currentToken.kind ) {
			case Token.INT:
        		T = new IntType (currentToken.GetSourcePos());
	    		break;
			case Token.BOOL:
            	T = new BoolType (currentToken.GetSourcePos());
	    		break;
			case Token.FLOAT:
            	T = new FloatType (currentToken.GetSourcePos());
	    		break;
			case Token.VOID:
            	T = new VoidType (currentToken.GetSourcePos());
	    		break;
			default:
	    	assert( false ); // unknown type :(
		}
	
		acceptIt();
		return T;
    }


    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseFunPart():
    //
    // FunPart ::= ( "(" ParamsList? ")" CompoundStmt )
    //
    ///////////////////////////////////////////////////////////////////////////////

    public Decl parseFunPart(Type T, ID Ident, SourcePos pos) throws SyntaxError {

        // We already know that the current token is "(".
        // Otherwise use accept() !
        acceptIt();
		Decl PDecl = parseParamsList(); // can also be empty...
		accept(Token.RIGHTPAREN);
		CompoundStmt CStmt = parseCompoundStmt();
        finish(pos);
		return new FunDecl (T, Ident, PDecl, CStmt, pos);
    }


    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseParamsList():
    //
    // ParamsList ::= ParameterDecl ( "," ParameterDecl ) *
    //
    ///////////////////////////////////////////////////////////////////////////////

    public Decl parseParamsList() throws SyntaxError {
        if (!isTypeSpecifier(currentToken.kind)) {
           return new EmptyFormalParamDecl(previousTokenPosition);
        }
        
		Decl PDecl = parseParameterDecl();
		
		if (currentToken.kind == Token.COMMA) {
            acceptIt();
        }
        
		return new FormalParamDeclSequence (PDecl,
                        parseParamsList(), previousTokenPosition);
    } 


    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseParameterDecl():
    //
    // ParameterDecl ::= (VOID|INT|BOOL|FLOAT) Declarator
    //
    ///////////////////////////////////////////////////////////////////////////////

    public Decl parseParameterDecl() throws SyntaxError {
    
		Type T = null;
        Decl D = null;

        SourcePos pos = new SourcePos();
        start( pos );
		if (isTypeSpecifier(currentToken.kind)) {
	    	T = parseTypeSpecifier();
		} else {
	    	syntaxError("Type specifier instead of % expected",
			Token.spell(currentToken.kind));
		}
		D = parseDeclarator(T, pos);
		return D;
    }


    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseDeclarator():
    //
    // Declarator ::= ID ( "[" INTLITERAL "]" )?
    //
    ///////////////////////////////////////////////////////////////////////////////

    public Decl parseDeclarator(Type T, SourcePos pos) throws SyntaxError {
	
		ID Ident = parseID();
		if (currentToken.kind == Token.LEFTBRACKET) {
	    	ArrayType ArrT = parseArrayIndexDecl(T);
            finish(pos);
	    	return new FormalParamDecl (ArrT, Ident, pos);
		}
        
		finish(pos);
	
		return new FormalParamDecl (T, Ident, pos);
    }


    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseVarPart():
    //
    // VarPart ::= ( "[" INTLITERAL "]" )?  ( "=" initializer ) ? ( "," init_decl)* ";"
    //
    ///////////////////////////////////////////////////////////////////////////////

    public DeclSequence parseVarPart(Type T, ID Ident) throws SyntaxError {
	
		Type theType = T;
        Decl D;
	//	Decl D1;
        DeclSequence Seq = null;
     // DeclSequence fSeq = null;
		Expr E = new EmptyExpr(previousTokenPosition);
		
	//	boolean flag = false;
		
		if (currentToken.kind == Token.LEFTBRACKET) {
	    	theType = parseArrayIndexDecl(T);
		}
	
		if (currentToken.kind == Token.ASSIGN) {
	    	acceptIt();
            // You can use the following code after you have implemented
            // parseInitializer():
	    	E = parseInitializer();
		}
        
		D = new VarDecl (theType, Ident, E, previousTokenPosition);

        // You can use the following code after implementatin of parseInitDecl():
		
		if ( currentToken.kind == Token.COMMA ) {
	    	acceptIt();
	    	Seq = new DeclSequence ( D, parseInitDecl( T ), previousTokenPosition );
		} else {
            Seq = new DeclSequence ( D, new EmptyDecl ( previousTokenPosition ), previousTokenPosition );
        }
        
		accept ( Token.SEMICOLON );
	
		return Seq;
    }

    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseInitDecl():
    //
    // init-decl ::= declarator ("=" initializer )?
    //
    ///////////////////////////////////////////////////////////////////////////////

	public DeclSequence parseInitDecl( Type T ) throws SyntaxError {
	
		SourcePos pos = new SourcePos( );
		SourcePos pos1 = new SourcePos( );
		DeclSequence Seq = null;
		VarDecl D = null;
		Expr E = null;
		boolean flag = false;

		start( pos );
		start( pos1 );
		ArrayType ArrT = null;
		ID Ident = parseID( );	
		
		if( currentToken.kind == Token.LEFTBRACKET ) {
			ArrT = parseArrayIndexDecl( T );	
			flag = true;
		}

		if( true == flag ) {	// Array Declaration

			if( currentToken.kind == Token.ASSIGN ) {
				acceptIt( );	// accepts a Token for '='
				E = parseInitializer( );
				D = new VarDecl( ArrT, Ident, E, pos );
				finish( pos );
			
				if( currentToken.kind == Token.COMMA ) {
					acceptIt( );	// accepts a Token for ','
					Seq = new DeclSequence( D, parseInitDecl( T ), pos1 );
					finish( pos1 );

					return Seq;
				}

				Seq = new DeclSequence( D, new EmptyDecl( previousTokenPosition ), pos1 );
				finish( pos1 );

				return Seq;
			}
			else { 
				
				D = new VarDecl( ArrT, Ident, new EmptyExpr( previousTokenPosition ), pos );
				finish( pos );

				if( currentToken.kind == Token.COMMA ) {
					acceptIt( );	// accepts a Token for ','
					Seq = new DeclSequence( D, parseInitDecl( T ), pos1 );
					finish( pos1 );

					return Seq;
				}

				Seq = new DeclSequence( D, new EmptyDecl( previousTokenPosition ), pos1 );
				finish( pos1 );

				return Seq;
			}

		} else { // false == flag, // Variable declaration but not an Array

			if( currentToken.kind == Token.ASSIGN ) {
				acceptIt( );	// accepts a Token for '='
				E = parseInitializer( );
				D = new VarDecl( T, Ident, E, pos );
				finish( pos );
			
				if( currentToken.kind == Token.COMMA ) {
					acceptIt( );	// accepts a Token for ','
					Seq = new DeclSequence( D, parseInitDecl( T ), pos1 );
					finish( pos1 );

					return Seq;
				}

				Seq = new DeclSequence( D, new EmptyDecl( previousTokenPosition ), pos1 );
				finish( pos1 );

				return Seq;
			}
			else { 
				
				D = new VarDecl( T, Ident, new EmptyExpr( previousTokenPosition ), pos );
				finish( pos );

				if( currentToken.kind == Token.COMMA ) {
					acceptIt( );	// accepts a Token for ','
					Seq = new DeclSequence( D, parseInitDecl( T ), pos1 );
					finish( pos1 );

					return Seq;
				}

				Seq = new DeclSequence( D, new EmptyDecl( previousTokenPosition ), pos1 );
				finish( pos1 );

				return Seq;
			}
		}


	}

    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseInitializer():
    //
    // Initializer ::= expr 
	//				|  "{" expr ( "," expr )* "}"
    //
    ///////////////////////////////////////////////////////////////////////////////

	/*
		Me

	*/

    public Expr parseInitializer() throws SyntaxError {

		ExprSequence tempExprSequence = null;
		ExprSequence fTempExprSeq = null;
		Expr E = null;

		SourcePos pos = new SourcePos( );

		if( currentToken.kind == Token.LEFTBRACE ) {

			acceptIt();	// accepts a Token for "{"
			start( pos );
			fTempExprSeq = tempExprSequence = new ExprSequence( parseExpr( ), null, pos );

			while( currentToken.kind == Token.COMMA ) {
				
				acceptIt(); // accepts a Token ","
				E = parseExpr( );
				tempExprSequence.rAST = new ExprSequence( E, null, previousTokenPosition );
				tempExprSequence = ( ExprSequence )tempExprSequence.rAST; 
			}
			
			tempExprSequence.rAST = new EmptyExpr( currentToken.GetSourcePos( ) );
			finish( pos );
			
			accept( Token.RIGHTBRACE );

			return fTempExprSeq;

		} else {
			return parseExpr( );
		}
	}


    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseExpr():
    //
    // Expr ::= or-expr
	// 
    ///////////////////////////////////////////////////////////////////////////////

	/*
		Me
	*/
    public Expr parseExpr() throws SyntaxError {

		return parseOrExpr();	

    }

    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseOrExpr():
    //
    // or-expr ::= and-expr ( "||" and-expr )*
	// 
    ///////////////////////////////////////////////////////////////////////////////

	/*
		Me
	*/
    public Expr parseOrExpr() throws SyntaxError {

		Expr temp = null;
		Expr temp1 = null;
		Expr ES = null;
		Expr fES = null;
		boolean flag = false;
		
		SourcePos pos = new SourcePos( );
/*		
		start( pos );

		temp = parseAndExpr();

		while( currentToken.kind == Token.OR ) {
			
			if( flag == false ) {
				flag = true;
				fES = ES = new BinaryExpr( temp, new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) ), null, pos );
				acceptIt();	// accepts a Token for "OR"
				temp1 = parseAndExpr( );
			}
			else {
				( ( BinaryExpr )ES ).rAST = new BinaryExpr( temp1, new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) ), 
															null, previousTokenPosition );
				acceptIt( ); // accepts a Token for 'OR'
				temp1 = parseAndExpr( );
				ES = ( ( BinaryExpr )ES ).rAST;
			}

		}

		if( flag == true ) {
			( ( BinaryExpr )ES ).rAST = temp1;
			finish( pos );
			return fES;
		}

		return temp;
		*/
/*
		start( pos );

		temp = parseAndExpr( );

		if( currentToken.kind == Token.OR ) {
			
			Operator OP = new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) );
			accept( Token.OR );
			ES = new BinaryExpr( temp, OP, parseOrExpr( ), pos );
			
			finish( pos );

			return ES;
		}
		else {
			return temp;
		}
*/

		start( pos );

		temp = parseAndExpr( );

		Operator OP = null;

		while( currentToken.kind == Token.OR ) {

			if( false == flag ) {
				flag = true;
				OP = new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) );
				accept( Token.OR );
				fES = ES = new BinaryExpr( temp, OP, parseAndExpr( ), pos );
			}
			else {
				OP = new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) );
				accept( Token.OR );
				fES = new BinaryExpr( ES, OP, parseAndExpr( ), pos );
				ES = null;
				ES = fES;
			}
		}

		finish( pos );

		if( true == flag ) {
			return fES;
		} else {
			return temp;
		}

    }
    
	///////////////////////////////////////////////////////////////////////////////
    //
    // parseAndExpr():
    //
    // and-expr ::= relational-expr ( "&&" relational-expr )*
	// 
    ///////////////////////////////////////////////////////////////////////////////
	/*
		Me
	*/
	public Expr parseAndExpr() throws SyntaxError {
		
		Expr E1 = null;
		Expr E2 = null;
		Expr ES = null;
		Expr fES = null;
		boolean flag = false;

		SourcePos pos = new SourcePos( );
/*
		start( pos );

		E1 = parseRelationalExpr( );

		while( currentToken.kind == Token.AND ) {
			if( flag == false ) {
				flag = true;
				fES = ES = new BinaryExpr( E1, new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) ), null, pos );
				acceptIt();	// accepts a Token for "AND"
				E2 = parseRelationalExpr( );
			}
			else {
				( ( BinaryExpr )ES ).rAST = new BinaryExpr( E2, new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) ), 
															null, previousTokenPosition );
				acceptIt( ); // accepts a Token for 'OR'
				E2 = parseRelationalExpr( );
				ES = ( ( BinaryExpr )ES ).rAST;
			}
		}

		if( flag == true ) {
			( ( BinaryExpr )ES ).rAST = E2;
			finish( pos );
			return fES;
		}

		return E1;

		*/
/*	
		start( pos );
		E1 = parseRelationalExpr( );

		if( currentToken.kind == Token.AND ) {
			
			Operator OP = new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) );
			accept( Token.AND );
			ES = new BinaryExpr( E1, OP, parseAndExpr( ), pos );
			finish( pos );

			return ES;
		}

		return E1;
*/
		start( pos );

		E1 = parseRelationalExpr( );

		Operator OP = null;

		while( currentToken.kind == Token.AND ) {

			if( false == flag ) {
				flag = true;
				OP = new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) );
				accept( Token.AND );
				fES = ES = new BinaryExpr( E1, OP, parseRelationalExpr( ), pos );
			}
			else {
				OP = new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) );
				accept( Token.AND );
				fES = new BinaryExpr( ES, OP, parseRelationalExpr( ), pos );
				ES = null;
				ES = fES;
			}
		}
		
		finish( pos );

		if( true == flag ) {
			return fES;
		} else {
			return E1;
		}
	}

    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseRelationalExpr():
    //
    // relational-expr ::= add-expr ( logical-operators add-expr )*  
	// 
    ///////////////////////////////////////////////////////////////////////////////

	/*
		Me
	*/
	public Expr parseRelationalExpr() throws SyntaxError {
		
		Expr E1 = null;
		Expr E2 = null;
		Expr ES = null;
		Expr fES = null;
		boolean flag = false;

		SourcePos pos = new SourcePos( );
/*
		start( pos );

		E1 = parseAddExpr( );

		while( isComparisonOP( currentToken.kind ) ) {
			if( flag == false ) {
				flag = true;
				fES = ES = new BinaryExpr( E1, new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) ), null, pos );
				acceptIt();	// accepts one of the Comparison operators such as "<", ">", etc..
				E2 = parseAddExpr();
			}
			else { 
				( ( BinaryExpr )ES ).rAST = new BinaryExpr( E2, new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) ), 
															null, previousTokenPosition );
				acceptIt( ); // accepts a Token for 'OR'
				E2 = parseAddExpr( );
				ES = ( ( BinaryExpr )ES ).rAST;
			}
		}

		if( flag == true ) {
			( ( BinaryExpr )ES ).rAST = E2;
			finish( pos );
			return fES;
		}

		return E1;

		*/
/*
		start( pos );
		E1 = parseAddExpr( );

		if( isComparisonOP( currentToken.kind ) ) {
			
			Operator OP = new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) );
			acceptIt( );
			ES = new BinaryExpr( E1, OP, parseRelationalExpr( ), pos );
			finish( pos );

			return ES;
		}

		return E1;
*/
		start( pos );

		E1 = parseAddExpr( );

		Operator OP = null;

		while( isComparisonOP( currentToken.kind ) ) {

			if( false == flag ) {
				flag = true;
				OP = new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) );
				acceptIt( );
				fES = ES = new BinaryExpr( E1, OP, parseAddExpr( ), pos );
			}
			else {
				OP = new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) );
				acceptIt( );
				fES = new BinaryExpr( ES, OP, parseAddExpr( ), pos );
				ES = null;
				ES = fES;
			}
		}
		
		finish( pos );

		if( true == flag ) {
			return fES;
		} else {
			return E1;
		}
	}

    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseAddExpr():
    //
    // add-expr ::= mult-expr ( ("+"|"-") mult-expr )* 
	// 
    ///////////////////////////////////////////////////////////////////////////////

	/*
		Me
	*/
	public Expr parseAddExpr() throws SyntaxError {
		
		Expr E1 = null;
		Expr E2 = null;
		Expr ES = null;
		Expr fES = null;
		boolean flag = false;

		SourcePos pos = new SourcePos( );
/*
		start( pos );

		E1 = parseMultExpr( );

		while(	currentToken.kind == Token.PLUS	||
				currentToken.kind == Token.MINUS ) {
		
			if( flag == false ) {
				flag = true;
				fES = ES = new BinaryExpr( E1, new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) ), null, pos );
				acceptIt();	// accepts one of both "PLUS" and "MINUS"
				E2 = parseMultExpr();
			}
			else {
				( ( BinaryExpr )ES ).rAST = new BinaryExpr( E2, new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) ), 
															null, previousTokenPosition );
				acceptIt( ); // accepts a Token for 'OR'
				E2 = parseMultExpr( );
				ES = ( ( BinaryExpr )ES ).rAST;	
			}
		}

		if( flag == true ) {
			( ( BinaryExpr )ES ).rAST = E2;
			finish( pos );
			return fES;
		}

		return E1;
*/
/*		
		start( pos );
		E1 = parseMultExpr( );

		if(	currentToken.kind == Token.PLUS ||
			currentToken.kind == Token.MINUS ) {

			Operator OP = new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) );
			acceptIt( );
			ES = new BinaryExpr( E1, OP, parseAddExpr( ), pos );
			finish( pos );

			return ES;
		}

		return E1;
*/
		start( pos );

		E1 = parseMultExpr( );

		Operator OP = null;

		while(	currentToken.kind == Token.PLUS		||
				currentToken.kind == Token.MINUS	) {

			if( false == flag ) {
				flag = true;
				OP = new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) );
				acceptIt( );
				fES = ES = new BinaryExpr( E1, OP, parseMultExpr( ), pos );
			}
			else {
				OP = new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) );
				acceptIt( );
				fES = new BinaryExpr( ES, OP, parseMultExpr( ), pos );
				ES = null;
				ES = fES;
			}
		}
		
		finish( pos );

		if( true == flag ) {
			return fES;
		} else {
			return E1;
		}
	}

    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseMultExpr():
    //
    // mult-expr ::= unary-expr ( ("*"|"/") unary-expr )* 
	// 
    ///////////////////////////////////////////////////////////////////////////////

	/*
		Me
	*/
	public Expr parseMultExpr() throws SyntaxError {

		Expr u1 = null;
		Expr u2 = null;
		Expr uES = null;
		Expr fuES = null;
		boolean flag = false;

		SourcePos pos = new SourcePos( );

		start( pos );
/*
	 	u1 = parseUnaryExpr( );

		while(	currentToken.kind == Token.TIMES	||
				currentToken.kind == Token.DIV ) {
		
			if( flag == false ) {
				flag = true;
				fuES = uES = new BinaryExpr( u1, new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) ), null, pos );
				acceptIt();	// accepts one of both "TIMES" and "DIV"
				u2 = parseUnaryExpr();
			}
			else {
				( ( BinaryExpr )uES ).rAST = new BinaryExpr( u2, new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) ), 
															null, previousTokenPosition );
				acceptIt( ); // accepts a Token for 'OR'
				u2 = parseUnaryExpr( );
				uES = ( ( BinaryExpr )uES ).rAST;	
			}
		}

		if( flag == true ) {
			( ( BinaryExpr )uES ).rAST = u2;
			finish( pos );
			return fuES;
		}
			
		return u1;
*/
/*
		start( pos );
		u1 = parseUnaryExpr( );

		if(	currentToken.kind == Token.TIMES	||
			currentToken.kind == Token.DIV ) {
	
			Operator OP = new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) );
			acceptIt( );
			uES = new BinaryExpr( u1, OP, parseMultExpr( ), pos );
			finish( pos );

			return uES;
		}

		return u1;
*/
		start( pos );

		u1 = parseUnaryExpr( );

		Operator OP = null;

		while(	currentToken.kind == Token.TIMES	||
				currentToken.kind == Token.DIV		) {

			if( false == flag ) {
				flag = true;
				OP = new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) );
				acceptIt( );
				fuES = uES = new BinaryExpr( u1, OP, parseUnaryExpr( ), pos );
			}
			else {
				OP = new Operator( currentToken.GetLexeme( ), currentToken.GetSourcePos( ) );
				acceptIt( );
				fuES = new BinaryExpr( uES, OP, parseUnaryExpr( ), pos );
				uES = null;
				uES = fuES;
			}
		}
		
		finish( pos );

		if( true == flag ) {
			return fuES;
		} else {
			return u1;
		}
	}

    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseUnaryExpr():
    //
    // UnaryExpr ::= ("+"|"-"|"!")* PrimaryExpr
    //
    ///////////////////////////////////////////////////////////////////////////////

    public Expr parseUnaryExpr() throws SyntaxError {
	
		while ( currentToken.kind == Token.PLUS		||
                currentToken.kind == Token.MINUS	||
                currentToken.kind == Token.NOT ) {
	    
			Operator opAST = new Operator ( currentToken.GetLexeme(), previousTokenPosition );
	    	acceptIt();
	    	return new UnaryExpr (opAST, parseUnaryExpr(), previousTokenPosition);
		}
	
		return parsePrimaryExpr();
    }


    ///////////////////////////////////////////////////////////////////////////////
    //
    // parsePrimaryExpr():
    //
    // PrimaryExpr ::= ID arglist?
    //              |  ID "[" expr "]"
    //              |  "(" expr ")"
    //              |  INTLITERAL | BOOLLITERAL | FLOATLITERAL | STRINGLITERAL
    //
    ///////////////////////////////////////////////////////////////////////////////

    public Expr parsePrimaryExpr() throws SyntaxError {
	
		/*
			Me
		*/
		
		Expr retExpr = null;
		Expr E1 = null;
		Expr E2 = null;
		Expr E3 = null;

		ID Ident;
		
		SourcePos pos = new SourcePos( );
		SourcePos pos_temp = new SourcePos( );

		IntLiteral		L1;
		FloatLiteral	L2;
		BoolLiteral		L3;
		StringLiteral	L4;
		
		if( currentToken.kind == Token.ID ) {

			start( pos );
			start( pos_temp );
			Ident = parseID();
				
			if( currentToken.kind == Token.LEFTBRACKET ) {
				
				finish( pos_temp );

				acceptIt(); // accepts a Token for "["			
				
				E1 = parseExpr( );
				
				accept( Token.RIGHTBRACKET );
				retExpr = new ArrayExpr( new VarExpr( Ident, pos_temp ), E1, pos );
				finish( pos );

			} else {
				if( currentToken.kind == Token.LEFTPAREN ) {

					E1 = parseArgList();
					retExpr = new CallExpr( Ident, E1, pos );
					finish( pos );

				}
				else {
					

					retExpr = new VarExpr( Ident, pos );
					
					finish( pos );
				}
			}

		} else if( currentToken.kind == Token.LEFTPAREN ) {
			
			acceptIt();	// accects '('
			E1 = parseExpr( );
			accept( Token.RIGHTPAREN );

			retExpr = E1;

		} else {
			
			if( isLiteral( currentToken.kind ) ) {
				
				pos = currentToken.GetSourcePos( );

				switch( currentToken.kind ) {
					
					case Token.INTLITERAL :
						L1 = new IntLiteral( currentToken.GetLexeme( ), pos );
						acceptIt(); // accepts one of the Literals such as string, integer, float, etc ..
						retExpr = new IntExpr( L1, pos );
						break;
					case Token.FLOATLITERAL :
						L2 = new FloatLiteral( currentToken.GetLexeme( ), pos );
						acceptIt(); // accepts one of the Literals such as string, integer, float, etc ..
						retExpr = new FloatExpr( L2, pos );
						break;
					case Token.BOOLLITERAL :
						L3 = new BoolLiteral( currentToken.GetLexeme( ), pos );
						acceptIt(); // accepts one of the Literals such as string, integer, float, etc ..
						retExpr = new BoolExpr( L3, pos );
						break;
					case Token.STRINGLITERAL :
						L4 = new StringLiteral( currentToken.GetLexeme( ), pos );
						acceptIt(); // accepts one of the Literals such as string, integer, float, etc ..
						retExpr = new StringExpr( L4, pos );
						break;
				}
			
			}
		}

		return retExpr;
    }

    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseStmt():
    //
    // Stmt ::= 
    //
    ///////////////////////////////////////////////////////////////////////////////

    public Stmt parseStmt() throws SyntaxError {

		Stmt S = null;
		ID Ident;
		Expr E1 = null;
		Expr E2 = null;
		Expr E3 = null;
		Expr temp = null;

		switch( currentToken.kind )
		{
			case Token.LEFTBRACE :
				{
					S = parseCompoundStmt();
					break;
				}
			case Token.IF :
				{
					S = parseIfStmt();
					break;
				}
			case Token.WHILE :
				{
					S = parseWhileStmt();
					break;
				}
			case Token.FOR :
				{
					S = parseForStmt();
					break;
				}
			case Token.RETURN :
				{
					S = parseReturnStmt();
					break;
				}
			case Token.ID :
				{
					SourcePos pos = new SourcePos( );

					pos = currentToken.GetSourcePos( );

					Ident = parseID( );	

					if( currentToken.kind == Token.LEFTPAREN ) {
				
						S = parseCallStmt( Ident, pos );
				
					}
					else {
						if( currentToken.kind == Token.LEFTBRACKET ) {
								
							acceptIt( ); // accepts a Token for "["

							E1 = parseExpr( );

							accept( Token.RIGHTBRACKET );
						
							E2 = new ArrayExpr( new VarExpr( Ident, previousTokenPosition ), E1, previousTokenPosition );

							//accept( Token.ASSIGN );
								
							S = parseAssignStmt( E2, pos );
						}
						else {
							E1 = new VarExpr( Ident, previousTokenPosition );						

						//	System.out.println( Ident.Lexeme );

							S = parseAssignStmt( E1, pos );
						}
					}
					
					break;
				}
			default :
				{
					S = new EmptyStmt( previousTokenPosition );
					break;
				}
		}

		return S;

    }

    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseCallStmt():
    //
    // Expr ::= ID arglist ";"
	// 
    ///////////////////////////////////////////////////////////////////////////////

	public CallStmt parseCallStmt( ID Ident, SourcePos pos ) throws SyntaxError {

		Expr args= parseArgList( );
		Expr callExpr = new CallExpr( Ident, args, previousTokenPosition );
		CallStmt S = new CallStmt( callExpr, pos );
		finish( pos );	

		accept( Token.SEMICOLON );

		return S;
	}

    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseIfStmt():
    //
    // Expr ::= 
	// 
    ///////////////////////////////////////////////////////////////////////////////
	public IfStmt parseIfStmt() throws SyntaxError {

		SourcePos pos = new SourcePos( );
		Expr E = null;
		Stmt S1 = null;
		Stmt S2 = null;
		IfStmt RS = null;

		start( pos );

		acceptIt();	// accepts a Token for "IF"
		accept( Token.LEFTPAREN );
		E = parseExpr();
		accept( Token.RIGHTPAREN );
		S1 = parseStmt();

		if( currentToken.kind == Token.ELSE ) {
			acceptIt();	// accepts a Token for "ELSE"
			S2 = parseStmt();
			finish( pos );
			return new IfStmt( E, S1, S2, pos );
		}

		RS = new IfStmt( E, S1, pos );

		finish( pos );

		return RS;

	}
    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseWhileStmt():
    //
    // Expr ::= 
	// 
    ///////////////////////////////////////////////////////////////////////////////
	public WhileStmt parseWhileStmt() throws SyntaxError {

		SourcePos pos = new SourcePos( );
		Expr E = null;
		Stmt S = null;
		WhileStmt SW = null;

		start( pos );
		acceptIt();	// accepts a Token for "WHILE"
		accept( Token.LEFTPAREN );
		E = parseExpr();
		accept( Token.RIGHTPAREN );
		S = parseStmt();

		SW = new WhileStmt( E, S, pos );
		finish( pos );

		return SW;
	}
    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseForStmt():
    //
    // Expr ::= 
	// 
    ///////////////////////////////////////////////////////////////////////////////
	public ForStmt parseForStmt() throws SyntaxError {

		SourcePos pos = new SourcePos( );
		Expr E1 = null;
		Expr E2 = null;
		Expr E3 = null;
		Stmt S = null;
		ForStmt SF = null;

		start( pos );

		acceptIt();	// accepts a Token for "FOR"
		accept( Token.LEFTPAREN );
		
		if( currentToken.kind == Token.ID ) {
			E1 = parseAsgnExpr();
		}
		else {
			E1 = new EmptyExpr( previousTokenPosition );
		}
		accept( Token.SEMICOLON );
		if( currentToken.kind != Token.SEMICOLON ) {
			E2 = parseExpr();
		}
		else {
			E2 = new EmptyExpr( previousTokenPosition );
		}
		accept( Token.SEMICOLON );
		if( currentToken.kind == Token.ID ) {
			E3 = parseAsgnExpr();
		}
		else {
			E3 = new EmptyExpr( previousTokenPosition );
		}
		accept( Token.RIGHTPAREN );
		S = parseStmt();
		SF = new ForStmt( E1, E2, E3, S, pos );
		finish( pos );

		return SF;
	}
    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseReturnStmt():
    //
    // Expr ::= 
	// 
    ///////////////////////////////////////////////////////////////////////////////
	public ReturnStmt parseReturnStmt() throws SyntaxError {

		SourcePos pos = new SourcePos( );
		Expr E = null;
		ReturnStmt RS = null;

		start( pos );
		acceptIt();	// accepts a Token for "RETURN"
		if( currentToken.kind != Token.SEMICOLON ) {
			E = parseExpr();
		}
		else {
			E = new EmptyExpr( previousTokenPosition );
		}
		accept( Token.SEMICOLON );
		RS = new ReturnStmt( E, pos );
		finish( pos );

		return RS;
	}


	private Stmt parseAssignStmt( Expr leftAST, SourcePos pos ) throws SyntaxError {

		Stmt S = null;
		Expr E = null;

		accept( Token.ASSIGN );	
		E = parseExpr( );

		S = new AssignStmt( leftAST, E, pos );
		finish( pos );

		accept( Token.SEMICOLON );
		
		return S;
	}

    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseCompoundStmt():
    //
    // CompoundStmt ::= "{" VariableDef* Stmt* "}"
    //
    ///////////////////////////////////////////////////////////////////////////////

    public Decl parseCompoundDecls () throws SyntaxError {
        
		if (!isTypeSpecifier(currentToken.kind)) {
           return new EmptyDecl (previousTokenPosition);
        }
        
		Type T = parseTypeSpec();
		ID Ident = parseID();
        DeclSequence Vars = parseVarPart(T, Ident);
        DeclSequence VarsTail = Vars.GetRightmostDeclSequenceNode();
        Decl RemainderDecls = parseCompoundDecls();
        VarsTail.SetRightSubtree (RemainderDecls);
        
		return Vars;       
    }

    public Stmt parseCompoundStmts () throws SyntaxError {
		
		if( !( currentToken.kind == Token.LEFTBRACE ||
               currentToken.kind == Token.IF ||
               currentToken.kind == Token.WHILE ||
               currentToken.kind == Token.FOR ||
               currentToken.kind == Token.RETURN ||
               currentToken.kind == Token.ID)
	   	  ) {
	    	return new EmptyStmt(previousTokenPosition);
		}
        
		Stmt S = null;
        
		// You can use the following code after implementation of parseStmt():
        S = parseStmt();
        
		return new StmtSequence (S, parseCompoundStmts(), previousTokenPosition);
    }

    public CompoundStmt parseCompoundStmt() throws SyntaxError {
        
		SourcePos pos = new SourcePos();
        start(pos);
	
		accept(Token.LEFTBRACE);
        
		Decl D = parseCompoundDecls();
        Stmt S = parseCompoundStmts();
	
		accept(Token.RIGHTBRACE);
        
		finish(pos);
        
		if ( (D.getClass() == EmptyDecl.class) &&
             (S.getClass() == EmptyStmt.class)) {
        	return new EmptyCompoundStmt (previousTokenPosition);
        } else {
	   		return new CompoundStmt (D, S, pos);
        }
    }


    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseArgList():
    //
    // ArgList ::= "(" ( arg ( "," arg )* )? ")"
    //
    ///////////////////////////////////////////////////////////////////////////////

    public Expr parseArgs() throws SyntaxError {
	
		if ( currentToken.kind == Token.RIGHTPAREN ) {
	    	return new  EmptyActualParam ( previousTokenPosition );
        } 

		Expr Params = null;
 /*
  * You can use the following code after you have implemented parseExpr() aso.:
  *
  *
  */      
		Params = new ActualParam ( parseExpr(), previousTokenPosition );
		
		if (currentToken.kind == Token.COMMA) {
			acceptIt();
        }
		
		return new ActualParamSequence (Params, parseArgs(), previousTokenPosition);
    }

    public Expr parseArgList() throws SyntaxError {
		
		accept(Token.LEFTPAREN);
        Expr Params = parseArgs();
		accept(Token.RIGHTPAREN);
	
		return Params;
    }


    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseID():
    //
    // ID (terminal)
    //
    ///////////////////////////////////////////////////////////////////////////////

    public ID parseID() throws SyntaxError {
	
		ID Ident = new ID(currentToken.GetLexeme(), currentToken.GetSourcePos());
		
		//System.out.println( currentToken.GetLexeme( ) );
		accept(Token.ID);
		return Ident;
    }


    ///////////////////////////////////////////////////////////////////////////////
    //
    // parseTypeSpecifier():
    //
    // VOID | INT | FLOAT | BOOL (all terminals)
    //
    ///////////////////////////////////////////////////////////////////////////////

    public Type parseTypeSpecifier() throws SyntaxError {
	
		Type T = null;
		
		switch (currentToken.kind)
		{
			case Token.INT:
	    	T = new IntType(currentToken.GetSourcePos());
	    	break;
		case Token.FLOAT:
	    	T = new FloatType(currentToken.GetSourcePos());
	    	break;
		case Token.BOOL:
	    	T = new BoolType(currentToken.GetSourcePos());
	    	break;
		case Token.VOID:
	    	T = new VoidType(currentToken.GetSourcePos());
	    	break;
		default:
	    	syntaxError("Type specifier expected", "");
		}
	
		acceptIt();
		return T;
    }

	/*
		Me
	*/
	public AssignExpr parseAsgnExpr() throws SyntaxError {

		SourcePos pos = new SourcePos( );
		AssignExpr E = null;
		
		start( pos );

		ID Ident = parseID( );

		Expr VE = new VarExpr( Ident, previousTokenPosition );
		
	///	acceptIt(); // accepts a Token for 'ID'
		accept( Token.ASSIGN );
		E = new AssignExpr( VE, parseExpr(), pos );

		finish( pos );

		return E;
	
	}

}	// The end of Parser class

/*
	The end
*/

